#include "stats.h"

sniff_stat*
add_new_node(sniff_stat* current)
{
	// Création d'un nouveau noeud de stats
	sniff_stat* new = (sniff_stat*) malloc(sizeof(sniff_stat));

	// Ajout en tête de liste
	new->next = current;

	return new;
}

void
free_stats(sniff_stat* stats)
{
	sniff_stat* next = stats;
	
	// On libère toute la mémoire allouée
	while (next != NULL)
	{
		next = stats->next;
		free(stats);
		stats = next;
	}
}

void
generate_stats(sniff_stat* stats, struct timeval* begin, struct timeval* end)
{
	// Variable de parcours
	sniff_stat* current = stats;
	
	long duration;
	int nb_packets      = 0;
	int nb_packets_ipv4 = 0;
	int nb_packets_ipv6 = 0;
	int nb_packets_tcp  = 0;
	int nb_packets_udp  = 0;
	
	GHashTable* trafic_ethernet = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_ipv4     = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_tcp_src  = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_tcp_dst  = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_udp_src  = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_udp_dst  = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	GHashTable* trafic_ipv6     = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
	
	// Variable de parcours
	network_trafic  trafic;
	
	while (current != NULL)
	{
		nb_packets++;
		
		add_key(trafic_ethernet, current->eth_type);
		
		if (current->is_ipv4)
		{
			nb_packets_ipv4++;
			
			add_key(trafic_ipv4, current->ipv4_proto);
			
			if (current->is_tcp)
			{
				nb_packets_tcp++;
				
				add_key(trafic_tcp_src, current->tcp_src);
				add_key(trafic_tcp_dst, current->tcp_dst);
			}
			else if (current->is_udp)
			{
				nb_packets_udp++;
				
				add_key(trafic_udp_src, current->udp_src);
				add_key(trafic_udp_dst, current->udp_dst);
			}
		}
		
		if (current->is_ipv6)
		{
			nb_packets_ipv6++;
		
			add_key(trafic_ipv6, current->ipv6_nh);
		}
		
		current = current->next;
	}
		
	printf("Duration of network analysis : ");
	
	duration = difftime_ms(end, begin)/1000.;
	print_duration(duration);
	
	printf("\n");
	
	// Affichage du trafic ethernet
	printf("Number of captured packets : %d [%.1f/s]\n",
	       nb_packets,
	       flow(nb_packets, (int)duration));
	
	trafic.nb_packets = nb_packets;
	trafic.duration   = duration;
	g_hash_table_foreach(trafic_ethernet, foreach_trafic_ethernet, (gpointer) &trafic);
	
	// Affichage du trafic IP(v4)
	printf("\nIPv4 packets : %d (%.1f%%)  [%.1f/s]\n",
	       nb_packets_ipv4,
	       proportion(nb_packets_ipv4, nb_packets),
	       flow(nb_packets_ipv4, (int)duration));
	
	trafic.nb_packets = nb_packets_ipv4;
	g_hash_table_foreach(trafic_ipv4, foreach_trafic_ipv4, (gpointer) &trafic);

	// Affichage du trafic TCP (IPv4) : source
	printf("\nTCP(v4) packets source :\n");
	trafic.nb_packets = nb_packets_tcp;
	g_hash_table_foreach(trafic_tcp_src, foreach_trafic_tcp, (gpointer) &trafic);
	
	// Affichage du trafic TCP (IPv4) : destination
	printf("\nTCP(v4) packets destination :\n");
	g_hash_table_foreach(trafic_tcp_dst, foreach_trafic_tcp, (gpointer) &trafic);

	// Affichage du trafice UDP (IPv4) : source
	printf("\nUDP(v4) packets source :\n");
	trafic.nb_packets = nb_packets_udp;
	g_hash_table_foreach(trafic_udp_src, foreach_trafic_udp, (gpointer) &trafic);
	
	// Affichage du trafic TCP (IPv4) : destination
	printf("\nUDP(v4) packets destination :\n");
	g_hash_table_foreach(trafic_udp_dst, foreach_trafic_udp, (gpointer) &trafic);

	// Affichage du trafic IPv6	
	printf("\nIPv6 packets : %d (%.1f%%)  [%.1f/s]\n",
	       nb_packets_ipv6,
	       proportion(nb_packets_ipv6, nb_packets),
	       flow(nb_packets_ipv6, (int)duration));
	
	trafic.nb_packets = nb_packets_ipv6;
	g_hash_table_foreach(trafic_ipv6, foreach_trafic_ipv6, (gpointer) &trafic);
	
	// On libère la mémoire
	g_hash_table_destroy(trafic_ethernet);
	g_hash_table_destroy(trafic_tcp_src);
	g_hash_table_destroy(trafic_tcp_dst);
	g_hash_table_destroy(trafic_udp_src);
	g_hash_table_destroy(trafic_udp_dst);
	g_hash_table_destroy(trafic_ipv4);
	g_hash_table_destroy(trafic_ipv6);
}

void
add_key(GHashTable* current, int _key)
{	
	gint* key   = g_new(gint, 1);
	gint* value = g_new(gint, 1);
	
	*key = _key;

	gint* item = (gint*) g_hash_table_lookup(current, key);
	
	if (item)
	{
		*value = *item + 1;
		g_hash_table_replace(current, key, value);
	}
	else
	{
		*value = 1;	
		g_hash_table_replace(current, key, value);
	}
}

void
foreach_trafic_ethernet(gpointer key, gpointer value, gpointer user_data)
{
	network_trafic* trafic = (network_trafic*) user_data;
	int* _key   = key;
	int* _value = value;
	
	printf("\t");
	print_eth_type(*_key);
	printf(" : %d (%.1f%%) [%.1f/s]\n",
	       *_value,
	       proportion(*_value, trafic->nb_packets),
	       flow(*_value, trafic->duration));
}

void
foreach_trafic_ipv4(gpointer key, gpointer value, gpointer user_data)
{
	network_trafic* trafic = (network_trafic*) user_data;
	int* _key   = key;
	int* _value = value;
	
	printf("\t");
	print_ipv4_protocol(*_key);
	printf(" : %d (%.1f%%) [%.1f/s]\n",
	       *_value,
	       proportion(*_value, trafic->nb_packets),
	       flow(*_value, trafic->duration));
}

void
foreach_trafic_tcp(gpointer key, gpointer value, gpointer user_data)
{
	network_trafic* trafic = (network_trafic*) user_data;
	int* _key   = key;
	int* _value = value;
	
	// Filtre
	if (proportion(*_value, trafic->nb_packets) >= 1)
	{
		printf("\t%d ", *_key);
		print_ipv4_service(*_key, "tcp");

		printf(": %d (%.1f%%) [%.1f/s]\n",
			   *_value,
			   proportion(*_value, trafic->nb_packets),
			   flow(*_value, trafic->duration));
	}
}

void
foreach_trafic_udp(gpointer key, gpointer value, gpointer user_data)
{
	network_trafic* trafic = (network_trafic*) user_data;
	int* _key   = key;
	int* _value = value;
	
	// Filtre
	if (proportion(*_value, trafic->nb_packets) >= 1)
	{
		printf("\t%d ", *_key);
		print_ipv4_service(*_key, "udp");

		printf(": %d (%.1f%%) [%.1f/s]\n",
			   *_value,
			   proportion(*_value, trafic->nb_packets),
			   flow(*_value, trafic->duration));
	}
}

void
foreach_trafic_ipv6(gpointer key, gpointer value, gpointer user_data)
{
	network_trafic* trafic = (network_trafic*) user_data;
	int* _key   = key;
	int* _value = value;
	
	printf("\t");
	print_ipv6_next_header(*_key);
	printf(" : %d (%.1f%%) [%.1f/s]\n",
	       *_value,
	       proportion(*_value, trafic->nb_packets),
	       flow(*_value, trafic->duration));
}

long
difftime_ms(struct timeval* newer, struct timeval* older)
{
	return (newer->tv_sec - older->tv_sec) * 1000 +
           (newer->tv_usec - older->tv_usec) / 1000;
}

void
print_duration(float duration)
{
	int h, m, s;
	
	s  = duration;
	m  = s / 60;
	h  = m / 60;
	
	s %= 60;
	m %= 60;
	
	printf("%dh %dm %ds", h, m, s);
}

float
proportion(int a, int b)
{
	return ((float)a / (float)b * 1.0) * 100;
}

float
flow(int nb, int duration)
{
	return ((float)nb / (float)duration * 1.0);
}

void generate_graph(sniff_stat* stats)
{
	sniff_stat* current = stats;
	
	int t_begin = (int)current->timestamp.tv_sec;
	int t_now   = 0;
	int t_old   = -1;
	
	int nb_packets      = 0;
	int nb_packets_ipv4 = 0;
	int nb_packets_ipv6 = 0;
	int nb_packets_tcp  = 0;
	int nb_packets_udp  = 0;
	
	FILE* file;
	
	// On ouvre le fichier de statistiques pour le graphe
	if ((file = fopen("traffic.plt", "w+")) == NULL)
	{
		printf("Error while creating network activity graph\n");
		return;
	}
	
	// On parcourt tous les paquets et on regarde le (nombre de paquet par catégorie) par seconde
	while (current != NULL)
	{
		t_now = abs((int)current->timestamp.tv_sec - t_begin);
		
		if (t_now != t_old)
		{
			// La seconde a changé, on écrit le nombre de paquets qu'on a trouvé pour chaque catégorie
			fprintf(file, "%d %d %d %d %d %d\n",
			              t_now,
			              nb_packets,
			              nb_packets_ipv4,
			              nb_packets_ipv6,
			              nb_packets_tcp,
			              nb_packets_udp);
			
			nb_packets = 0;
			nb_packets_ipv4 = 0;
			nb_packets_ipv6 = 0;
			nb_packets_tcp  = 0;
			nb_packets_udp  = 0;
			
			t_old = t_now;
		}
		
		nb_packets++;
		
		if (current->is_ipv4)
		{
			nb_packets_ipv4++;
		}
		
		if (current->is_ipv6)
		{
			nb_packets_ipv6++;
		}
		
		if (current->is_tcp)
		{
			nb_packets_tcp++;
		}
		
		if (current->is_udp)
		{
			nb_packets_udp++;
		}
		
		current = current->next;
	}
	
	fprintf(file, "%d %d %d %d %d %d\n", t_now+1, 0, 0, 0, 0, 0);
	fclose(file);
	printf("* A graph of network activity is available by making \"gnuplot gp_analysis.gp -pause\" in your terminal\n"); 	
}

